<template>
  <div class="home">
    <!-- 左侧模块菜单 -->
    <div class="menu-bar">
      <h2>模块列表</h2>
      <!-- 模块列表 -->
      <div class="menu-list">
        <div
          v-for="item in moduleList"
          :key="item.id"
          draggable="true"
          @dragend="handleDragEnd($event, item)"
        >
          <p>{{item.name}}</p>
        </div>
      </div>
    </div>
    <!-- 画布部分 -->
    <div class="canvas-card">
      <div
        id="container"
        @dragover="dragoverDiv"
      ></div>
    </div>
  </div>
</template>

<script>
import { Graph } from '@antv/x6'
import Tools from '@/assets/js/graphTools.js'
export default {
  data() {
    return {
      moduleList: [
        {
          id: 1,
          name: '开始模块',
          type: 'initial' // 初始模块(用于区分样式)
        },
        {
          id: 2,
          name: '结束模块',
          type: 'initial'
        },
        {
          id: 3,
          name: '逻辑模块1',
          type: 'logic' // 逻辑模块(用于区分样式)
        },
        {
          id: 4,
          name: '逻辑模块2',
          type: 'logic'
        }
      ], // 列表可拖动模块
      graph: null, // 画布实例对象
      curSelectNode: null, // 当前选中的节点
    }
  },
  mounted() {
    this.initGraph()
  },
  methods: {
    // 初始化流程图画布
    initGraph() {
      let container = document.getElementById('container')
      this.graph = new Graph({
        container: container, // 画布容器
        width: container.offsetWidth, // 画布宽
        height: container.offsetHeight, // 画布高
        background: false, // 背景（透明）
        snapline: true, // 对齐线
        // 配置连线规则
        connecting: {
          snap: true, // 自动吸附
          allowBlank: false, //是否允许连接到画布空白位置的点
          allowMulti: false, //是否允许在相同的起始节点和终止之间创建多条边
          allowLoop: false, //是否允许创建循环连线，即边的起始节点和终止节点为同一节点
          highlight: true, //拖动边时，是否高亮显示所有可用的节点
          validateEdge({ edge, type, previous }) {
            // 连线时设置折线
            edge.setRouter({
              name: 'er',
            })
            // 设置连线样式
            edge.setAttrs({
              line: {
                stroke: '#275da3',
                strokeWidth: 4,
              },
            })
            return true
          },
        },
        panning: {
          enabled: true,
        },
        mousewheel: {
          enabled: true, // 支持滚动放大缩小
        },
        grid: {
          type: 'mesh',
          size: 20,      // 网格大小 10px
          visible: true, // 渲染网格背景
          args: {
            color: '#eeeeee', // 网格线/点颜色
            thickness: 2,     // 网格线宽度/网格点大小
          },
        },
      })
      this.nodeAddEvent()
    },
    // 生成节点函数
    addHandleNode(x, y, id, name, type) {
      type === 'initial'
        ?
        this.graph.addNode(Tools.initInitialNode(x, y, id, name, type))
        :
        this.graph.addNode(Tools.initLogicNode(x, y, id, name, type))
    },
    nodeAddEvent() {
      // 节点绑定点击事件
      this.graph.on('node:click', ({ e, x, y, node, view }) => {
        // 判断是否有选中过节点
        if (this.curSelectNode) {
          // 移除选中状态
          this.curSelectNode.removeTools()
          // 判断两次选中节点是否相同
          if (this.curSelectNode !== node) {
            node.addTools([{
              name: 'boundary',
              args: {
                attrs: {
                  fill: '#16B8AA',
                  stroke: '#2F80EB',
                  strokeWidth: 1,
                  fillOpacity: 0.1
                }
              }
            }, {
              name: 'button-remove',
              args: {
                x: '100%',
                y: 0,
                offset: {
                  x: 0,
                  y: 0
                }
              }
            }])
            this.curSelectNode = node
          } else {
            this.curSelectNode = null
          }
        } else {
          this.curSelectNode = node
          node.addTools([{
            name: 'boundary',
            args: {
              attrs: {
                fill: '#16B8AA',
                stroke: '#2F80EB',
                strokeWidth: 1,
                fillOpacity: 0.1
              }
            }
          }, {
            name: 'button-remove',
            args: {
              x: '100%',
              y: 0,
              offset: {
                x: 0,
                y: 0
              }
            }
          }])
        }
      })
      // 连线绑定悬浮事件
      this.graph.on('cell:mouseenter', ({ cell }) => {
        if (cell.shape == 'edge') {
          cell.addTools([
            {
              name: 'button-remove',
              args: {
                x: '100%',
                y: 0,
                offset: {
                  x: 0,
                  y: 0
                },
              },
            }])
          cell.setAttrs({
            line: {
              stroke: '#409EFF',
            },
          })
          cell.zIndex = 99
        }
      })
      this.graph.on('cell:mouseleave', ({ cell }) => {
        if (cell.shape === 'edge') {
          cell.removeTools()
          cell.setAttrs({
            line: {
              stroke: '#275da3',
            },
          })
          cell.zIndex = 1
        }
      })
    },
    // 拖动后松开鼠标触发事件
    handleDragEnd(e, item) {
      this.addHandleNode(e.pageX - 240, e.pageY - 40, new Date().getTime(), item.name, item.type)
    },
    // 拖动节点到画布中鼠标样式变为可拖动状态
    dragoverDiv(ev) {
      ev.preventDefault()
    },
  },
}
</script>
<style lang="less" scoped>
.home {
  width: 100%;
  height: 100%;
  display: flex;
  padding: 20px;
  box-sizing: border-box;
  background: #eaeaea;
  > div {
    background: #ffffff;
    border-radius: 5px;
  }
  // 模块列表部分
  .menu-bar {
    width: 200px;
    height: 100%;
    margin-right: 20px;
    display: flex;
    flex-direction: column;
    h2 {
      width: 100%;
      font-size: 20px;
      padding: 10px;
      box-sizing: border-box;
      margin: 0;
    }
    .menu-list {
      height: 0;
      flex: 1;
      overflow: auto;
      padding: 0 10px;
      box-sizing: border-box;
      > div {
        border: 2px dashed #eaeaea;
        margin-bottom: 10px;
        border-radius: 5px;
        padding: 0 10px;
        box-sizing: border-box;
        cursor: pointer;
      }
    }
  }
  // 画布部分
  .canvas-card {
    width: 0;
    flex: 1;
    height: 100%;
    padding: 20px;
    box-sizing: border-box;
    > div {
      width: 100%;
      height: 100%;
      border: 2px dashed #eaeaea;
    }
  }
}
</style>
<style lang="less">
// 其中节点样式加到没有scoped包裹的style标签中，否则样式不生效
// 初始节点样式
.custom_node_initial {
  width: 100%;
  height: 100%;
  display: flex;
  border-radius: 3px;
  background: rgba(22, 184, 169, 0.6);
  flex-direction: column;
  overflow: hidden;
  > div {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 5px;
    box-sizing: border-box;
    border: 5px solid rgba(47, 128, 235, 0.6);
    i {
      line-height: 22px;
      font-size: 18px;
      color: #ffffff;
      display: flex;
      align-items: center;
      margin-right: 5px;
      justify-content: center;
      font-style: normal;
    }
    p {
      color: #ffffff;
      font-size: 16px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }
}
// 逻辑节点样式
.custom_node_logic {
  width: 100%;
  height: 100%;
  display: flex;
  background: rgba(47, 128, 235, 0.5);
  flex-direction: column;
  overflow: hidden;
  border-radius: 5px;
  > div {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 5px;
    box-sizing: border-box;
    border: 5px solid rgba(22, 184, 169, 0.5);
    border-radius: 5px;
    line-height: 22px;
    i {
      line-height: 22px;
      font-size: 18px;
      color: #b5cde9;
      margin-right: 5px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-style: normal;
    }
    p {
      color: #ffffff;
      font-size: 14px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }
}
</style>
